package com.iotechn.unimall.admin.api.goods;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.entity.Column;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.google.gson.Gson;
import com.iotechn.unimall.biz.service.goods.GoodsBizService;
import com.iotechn.unimall.core.exception.AdminServiceException;
import com.iotechn.unimall.core.exception.ExceptionDefinition;
import com.iotechn.unimall.core.exception.ServiceException;
import com.iotechn.unimall.data.component.CacheComponent;
import com.iotechn.unimall.data.domain.*;
import com.iotechn.unimall.data.dto.GoodsStatisticsDTO;
import com.iotechn.unimall.data.dto.goods.SpuDTO;
import com.iotechn.unimall.data.dto.goods.SpuTreeNodeDTO;
import com.iotechn.unimall.data.enums.BizType;
import com.iotechn.unimall.data.enums.OrderStatusType;
import com.iotechn.unimall.data.enums.SpuStatusType;
import com.iotechn.unimall.data.mapper.*;
import com.iotechn.unimall.data.model.Page;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.CollectionUtils;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Created by rize on 2019/7/11.
 */
@Service
public class AdminGoodsServiceImpl implements AdminGoodsService {

    @Autowired
    private CategoryMapper categoryMapper;

    @Autowired
    private SpuMapper spuMapper;

    @Autowired
    private SkuMapper skuMapper;

    @Autowired
    private SpuAttributeMapper spuAttributeMapper;

    @Autowired
    private ImgMapper imgMapper;

    @Autowired
    private CartMapper cartMapper;

    @Autowired
    private OrderSkuMapper orderSkuMapper;

    @Autowired
    private GoodsBizService goodsBizService;

    @Autowired
    private CacheComponent cacheComponent;

    @Autowired
    private TransactionTemplate transactionTemplate;

    @Autowired
    private PlatformTransactionManager platformTransactionManager;

    private static final Column[] spuBaseColumns = {
            Column.create().column("id"),
            Column.create().column("original_price").as("originalPrice"),
            Column.create().column("price"),
            Column.create().column("vip_price").as("vipPrice"),
            Column.create().column("title"),
            Column.create().column("sales"),
            Column.create().column("img"),
            Column.create().column("description"),
            Column.create().column("category_id").as("categoryId"),
            Column.create().column("freight_template_id").as("freightTemplateId"),
            Column.create().column("unit"),
            Column.create().column("status")};

    /**
     * 后台低频接口， 无需缓存
     *
     * @return
     * @throws ServiceException
     */
    public List<SpuTreeNodeDTO> getSpuBigTree(Long adminId) throws ServiceException {
        List<CategoryDO> categoryDOS = categoryMapper.selectList(new EntityWrapper<CategoryDO>().orderBy("level"));
        List<SpuDO> spuDOS = spuMapper.getSpuTitleAll();
        List<SpuTreeNodeDTO> list = new ArrayList<>();
        Integer recordLevelOne = 0;
        Integer recordLevelTwo = 0;
        for (int i = 0; i < categoryDOS.size(); i++) {
            if (i != 0 && categoryDOS.get(i - 1).getLevel().equals(0) && categoryDOS.get(i).getLevel().equals(1)) {
                recordLevelOne = i;
            }
            if (i != 0 && categoryDOS.get(i - 1).getLevel().equals(1) && categoryDOS.get(i).getLevel().equals(2)) {
                recordLevelTwo = i;
                break;
            }
        }

        for (int i = 0; i < recordLevelOne; i++) {
            CategoryDO categoryOnI = categoryDOS.get(i);    //一级类目
            SpuTreeNodeDTO dtoOnI = new SpuTreeNodeDTO();
            dtoOnI.setLabel(categoryOnI.getTitle());
            dtoOnI.setValue("C_" + categoryOnI.getId());
            dtoOnI.setId(categoryOnI.getId());
            dtoOnI.setChildren(new LinkedList<>());
            for (int j = recordLevelOne; j < recordLevelTwo; j++) {
                CategoryDO categoryOnJ = categoryDOS.get(j);    //二级类目
                if (!categoryOnJ.getParentId().equals(dtoOnI.getId())) {
                    continue;
                }

                SpuTreeNodeDTO dtoOnJ = new SpuTreeNodeDTO();
                dtoOnJ.setLabel(categoryOnJ.getTitle());
                dtoOnJ.setValue("C_" + categoryOnJ.getId());
                dtoOnJ.setId(categoryOnJ.getId());
                dtoOnJ.setChildren(new LinkedList<>());

                for (int p = recordLevelTwo; p < categoryDOS.size(); p++) {
                    CategoryDO categoryOnP = categoryDOS.get(p);    //三级类目
                    if (!categoryOnP.getParentId().equals(dtoOnJ.getId())) {
                        continue;
                    }

                    SpuTreeNodeDTO dtoOnP = new SpuTreeNodeDTO();
                    dtoOnP.setLabel(categoryOnP.getTitle());
                    dtoOnP.setValue("C_" + categoryOnP.getId());
                    dtoOnP.setId(categoryOnP.getId());
                    dtoOnP.setChildren(new LinkedList<>());

                    for (int k = 0; k < spuDOS.size(); k++) {
                        if (k != 0 && spuDOS.get(k - 1).getCategoryId().equals(dtoOnP.getId()) && !spuDOS.get(k).getCategoryId().equals(dtoOnP.getId())) {
                            break;
                        }
                        SpuDO spuDO = spuDOS.get(k);        //商品
                        if (spuDO.getCategoryId().equals(dtoOnP.getId())) {
                            SpuTreeNodeDTO dtoOnK = new SpuTreeNodeDTO();
                            dtoOnK.setLabel(spuDO.getTitle());
                            dtoOnK.setValue("G_" + spuDO.getId());
                            dtoOnK.setId(spuDO.getId());
                            dtoOnK.setChildren(new LinkedList<>());
                            dtoOnP.getChildren().add(dtoOnK);
                        }
                    }
                    dtoOnJ.getChildren().add(dtoOnP);
                }
                dtoOnI.getChildren().add(dtoOnJ);
            }
            list.add(dtoOnI);
        }
        return list;
    }
    @Override

    public List<SpuAttributeDO> spuAttributeList()  {
    //获取商品属性
        List<SpuAttributeDO> spuAttributeList = spuAttributeMapper.selectList(new EntityWrapper<SpuAttributeDO>());
        return spuAttributeList;

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String create(SpuDTO spuDTO, Long adminId) throws ServiceException {

        Date now = new Date();
        SpuDO spuDO = new SpuDO();
        BeanUtils.copyProperties(spuDTO, spuDO);
        Gson gson=new Gson();
        spuDO.setFile(gson.toJson(spuDTO.getFile()));
        if(spuDTO.getImgList()!=null && spuDTO.getImgList().size()>0){
            String imgs = String.join(",",spuDTO.getImgList());
            spuDO.setImg(imgs);
        }

        spuDO.setGmtUpdate(now);
        spuDO.setGmtCreate(now);
        spuDO.setAdminId(adminId);
        spuDO.setStatus(1);
        if (spuDO.getSales() != null) {
            spuDO.setSales(spuDO.getSales());
        } else {
            spuDO.setSales(0);
        }
        spuMapper.insert(spuDO);
        spuDTO.setId(spuDO.getId());

        //插入IMG
        insertSpuImg(spuDTO, spuDO.getId(), now);
        goodsBizService.clearGoodsCache(spuDO.getId());
        pluginUpdateInvoke(spuDO.getId());

        cacheComponent.delPrefixKey(GoodsBizService.CA_SPU_PAGE_PREFIX);
        return "ok";
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String edit(SpuDTO spuDTO, Long adminId) throws ServiceException {
        if (spuDTO.getId() == null) {
            throw new AdminServiceException(ExceptionDefinition.PARAM_CHECK_FAILED);
        }

        Date now = new Date();
        SpuDO spuDO = new SpuDO();
        BeanUtils.copyProperties(spuDTO, spuDO);
        Gson gson=new Gson();
        spuDO.setFile(gson.toJson(spuDTO.getFileList()));
        if(spuDTO.getImgList()!=null && spuDTO.getImgList().size()>0){
            String imgs = spuDTO.getImgList().stream().collect(Collectors.joining(","));
            spuDO.setImg(imgs);
        }
        spuDO.setGmtUpdate(now);
        spuMapper.updateById(spuDO);

        imgMapper.delete(new EntityWrapper<ImgDO>().eq("biz_id", spuDO.getId()).eq("biz_type", BizType.GOODS.getCode()));
        //插入IMG
        insertSpuImg(spuDTO, spuDO.getId(), now);
        goodsBizService.clearGoodsCache(spuDTO.getId());
        pluginUpdateInvoke(spuDTO.getId());

        cacheComponent.delPrefixKey(GoodsBizService.CA_SPU_PAGE_PREFIX);
        return "ok";
    }

    private void insertSpuAttribute(SpuDTO spuDTO, Date now) {
        if (!CollectionUtils.isEmpty(spuDTO.getAttributeList())) {
            for (SpuAttributeDO spuAttributeDO : spuDTO.getAttributeList()) {
                spuAttributeDO.setSpuId(spuDTO.getId());
                spuAttributeDO.setGmtUpdate(now);
                spuAttributeDO.setGmtCreate(now);
                spuAttributeMapper.insert(spuAttributeDO);
            }
        }
    }

    private void insertSpuImg(SpuDTO spuDTO, Long bizId, Date now) {
        List<String> imgList = spuDTO.getImgList();
        if(imgList!=null && imgList.size()>0){
            List<ImgDO> imgDOList = imgList.stream().map(item -> {
                ImgDO imgDO = new ImgDO();
                imgDO.setBizType(BizType.GOODS.getCode());
                imgDO.setBizId(bizId);
                imgDO.setUrl(item);
                imgDO.setGmtCreate(now);
                imgDO.setGmtUpdate(now);
                return imgDO;
            }).collect(Collectors.toList());
            imgMapper.insertImgs(imgDOList);
        }

    }

    @Override
    public Page<SpuDTO> list(Long shopId, String shopName, Integer page, Integer limit, Long adminId) throws ServiceException {
        List<SpuDTO> spuDOS = spuMapper.getSpuDTOList(shopId, shopName, (page - 1) * limit, limit);
        for(SpuDTO spuDTO:spuDOS){
            if(spuDTO.getImg()!=null && !spuDTO.getImg().equals("")){
                String[] arr = spuDTO.getImg().split(",");
                List<String> list = Arrays.asList(arr);
                spuDTO.setImgList(list);
             }
        }

        Integer count = spuMapper.getSpuDTOListCount(shopId, shopName);
        Page<SpuDTO> dtoPage = new Page<>(spuDOS, page, limit, count);

        return dtoPage;
    }

    @Override
    public SpuDTO detail(Long spuId, Long adminId) throws ServiceException {
        return goodsBizService.getGoods(spuId, 0l);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String delete(Long spuId, Long adminId) throws ServiceException {
        if (spuMapper.deleteById(spuId) <= 0) {
            throw new AdminServiceException(ExceptionDefinition.GOODS_NOT_EXIST);
        }
        goodsBizService.clearGoodsCache(spuId);
        pluginUpdateInvoke(spuId);

        cacheComponent.delPrefixKey(GoodsBizService.CA_SPU_PAGE_PREFIX);
        return "ok";
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String batchDelete(String idsJson, Long adminId) throws ServiceException {
        List<Long> ids = JSONObject.parseArray(idsJson, Long.class);
        if (CollectionUtils.isEmpty(ids)) {
            throw new AdminServiceException(ExceptionDefinition.GOODS_NOT_EXIST);
        }
        if (spuMapper.delete(new EntityWrapper<SpuDO>().in("id", ids)) <= 0) {
            throw new AdminServiceException(ExceptionDefinition.GOODS_NOT_EXIST);
        }
        List<Long> skuIds = skuMapper.selectSkuIdsBySpuIds(ids);
         imgMapper.delete(new EntityWrapper<ImgDO>().in("biz_id", ids).eq("biz_type", BizType.GOODS.getCode()));
         for (Long spuId : ids) {
            goodsBizService.clearGoodsCache(spuId);
        }
        return "ok";
    }

    private void pluginUpdateInvoke(Long spuId) {
        /*List<IPluginUpdateGoods> plugins = pluginsManager.getPlugins(IPluginUpdateGoods.class);
        if (!CollectionUtils.isEmpty(plugins)) {
            for (IPluginUpdateGoods updateGoods : plugins) {
                updateGoods.invokeGoodsUpdate(spuId);
            }
        }*/
    }

    @Override
    public List<SpuDO> searchSpu(Long shopId,Long categoryId, String title, Long adminId) throws ServiceException {
        return spuMapper.search(shopId,categoryId,title);
    }

    @Override
    public List<GoodsStatisticsDTO> statistics(Long gmtStart, Long gmtEnd, String idsJson, String statusJson, Long adminId) throws ServiceException {
        List<Integer> allStatus = JSONObject.parseArray(statusJson, Integer.class);
        // 将status分层两组
        List<Integer> refundStatus = allStatus.stream().filter(item -> (item == OrderStatusType.REFUNDED.getCode()
                || item == OrderStatusType.REFUNDING.getCode())).collect(Collectors.toList());
        // 退款状态分组
        List<Long> spuIds = JSONObject.parseArray(idsJson, Long.class);
        // 1.查出所有状态的
        List<GoodsStatisticsDTO> allGoodsStatistics;
        List<GoodsStatisticsDTO> refundStatistics;
        if (gmtStart != null && gmtEnd != null) {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String gmtStartStr = sdf.format(new Date(gmtStart));
            String gmtEndStr = sdf.format(new Date(gmtEnd));
            allGoodsStatistics = orderSkuMapper.goodsStatistics(spuIds, allStatus, gmtStartStr, gmtEndStr);
            refundStatistics = orderSkuMapper.goodsStatistics(spuIds, refundStatus, gmtStartStr, gmtEndStr);
        } else {
            allGoodsStatistics = orderSkuMapper.goodsStatistics(spuIds, allStatus, null, null);
            refundStatistics = orderSkuMapper.goodsStatistics(spuIds, refundStatus, null, null);
        }
        // 将两表汇总
        for (GoodsStatisticsDTO allItem : allGoodsStatistics) {
            for (int i = 0; i < refundStatistics.size(); i++) {
                GoodsStatisticsDTO refundDTO = refundStatistics.get(i);
                if (allItem.equals(refundDTO)) {
                    // 若两个是同一个
                    allItem.setRefundPrice(refundDTO.getTotalPrice());
                    allItem.setConfirmPrice(allItem.getTotalPrice() - refundDTO.getTotalPrice());
                }
            }
            if (allItem.getRefundPrice() == null) {
                allItem.setRefundPrice(0);
                allItem.setConfirmPrice(allItem.getTotalPrice());
            }

        }
        return allGoodsStatistics;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public SpuDTO freezeOrActivation(Long spuId, Integer status, Long adminId) throws ServiceException {
        SpuDO spuDO = spuMapper.selectById(spuId);

        if (spuDO == null) {
            throw new AdminServiceException(ExceptionDefinition.GOODS_NOT_EXIST);
        }

        status = status <= SpuStatusType.STOCK.getCode() ? SpuStatusType.STOCK.getCode() : SpuStatusType.SELLING.getCode();

        if (spuDO.getStatus().intValue() == status.intValue()) {
            throw new AdminServiceException(ExceptionDefinition.GOODS_NEED_STATUS_ERROR);
        }

        spuDO.setStatus(status);
        spuDO.setGmtUpdate(new Date());
        if (spuMapper.updateById(spuDO) <= 0) {
            throw new AdminServiceException(ExceptionDefinition.GOODS_UPDATE_SQL_FAILED);
        }

        SpuDTO spuDTO = new SpuDTO();
        BeanUtils.copyProperties(spuDO, spuDTO);
        List<SkuDO> skuDOList = skuMapper.selectList(new EntityWrapper<SkuDO>().eq("spu_id", spuDO.getId()));
        spuDTO.setSkuList(skuDOList);

        cacheComponent.delPrefixKey(GoodsBizService.CA_SPU_PAGE_PREFIX);
        return spuDTO;
    }
}
 